package bat.iog.hytool.nio;

import java.io.*;
import java.nio.ByteBuffer;

public class IoAndNio {

    public static void main(String[] args){

    }

    /**
     * 路径的分类
     *    相对路径：相较于某个路径下，指明的路径。
     *    绝对路径：包含盘符在内的文件或文件目录的路径
     * 1.File类的理解
     *    1. File类的一个对象，代表一个文件或一个文件目录(俗称：文件夹)
     *    2. File类声明在java.io包下
     *    3. File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法，
     *       并未涉及到写入或读取文件内容的操作。如果需要读取或写入文件内容，必须使用IO流来完成。
     *    4. 后续File类的对象常会作为参数传递到流的构造器中，指明读取或写入的"终点".
     * 2.File类的常用方法
     *    ...
     * 3.流的分类
     *    1.操作数据单位：字节流、字符流
     *    2.数据的流向：输入流、输出流
     *    3.流的角色：节点流、处理流
     * 4.输入过程
     *   ① 创建File类的对象，指明读取的数据的来源。（要求此文件一定要存在）
     *   ② 创建相应的输入流，将File类的对象作为参数，传入流的构造器中
     *   ③ 具体的读入过程：
     *     创建相应的byte[] 或 char[]。
     *   ④ 关闭流资源
     *     说明：程序中出现的异常需要使用try-catch-finally处理。
     * 5.输出过程
     *   ① 创建File类的对象，指明写出的数据的位置。（不要求此文件一定要存在）
     *   ② 创建相应的输出流，将File类的对象作为参数，传入流的构造器中
     *   ③ 具体的写出过程：
     *     write(char[]/byte[] buffer,0,len)
     *   ④ 关闭流资源
     *     说明：程序中出现的异常需要使用try-catch-finally处理。
     * 6.缓冲流涉及到的类
     *   BufferedInputStream
     *   BufferedOutputStream
     *   BufferedReader
     *   BufferedWriter
     * 7.缓冲流作用：
     *   作用：提供流的读取、写入的速度
     *   提高读写速度的原因：内部提供了一个缓冲区。默认情况下是8kb
     * 8.转换流涉及到的类：属于字符流
     *   1.InputStreamReader：将一个字节的输入流转换为字符的输入流
     *      解码：字节、字节数组  --->字符数组、字符串
     *   2.OutputStreamWriter：将一个字符的输出流转换为字节的输出流
     *      编码：字符数组、字符串 ---> 字节、字节数组
     * 9.转换流作用：
     *   提供字节流与字符流之间的转换
     * 注：文件编码的方式（比如：GBK），决定了解析时使用的字符集（也只能是GBK）。
     * 10.对象流：ObjectInputStream 和 ObjectOutputStream
     * 11.对象流作用：
     *   ObjectOutputStream:内存中的对象--->存储中的文件、通过网络传输出去：序列化过程
     *   ObjectInputStream:存储中的文件、通过网络接收过来 --->内存中的对象：反序列化过程
     * 12.对象的序列化机制：
     *   对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流，从而允许把这种二进制流持久地保存在磁盘
     *   上，或通过网络将这种二进制流传输到另一个网络节点。//当其它程序获取了这种二进制流，就可以恢复成原来的
     *   Java对象
     * 13.实现序列化的对象所属的类需要满足：
     *   1.需要实现接口：Serializable
     *   2.当前类提供一个全局常量：serialVersionUID
     *   3.除了当前Person类需要实现Serializable接口之外，还必须保证其内部所属性也必须是可序列化的。（默认情况下，基本数据类型可序列化）
     *   4.ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
     */
    public void Io(){
        //字符流
        FileReader fr = null;
        FileWriter fw = null;
        try {
            //1.创建File类的对象，指明读入和写出的文件
            File srcFile = new File("hello.txt");
            File destFile = new File("hello2.txt");
            //不能使用字符流来处理图片等字节数据
//            File srcFile = new File("爱情与友情.jpg");
//            File destFile = new File("爱情与友情1.jpg");
            //2.创建输入流和输出流的对象
            fr = new FileReader(srcFile);
            fw = new FileWriter(destFile);
            //3.数据的读入和写出操作
            char[] cbuf = new char[5];
            int len;//记录每次读入到cbuf数组中的字符的个数
            while((len = fr.read(cbuf)) != -1){
                //每次写出len个字符
                fw.write(cbuf,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.关闭流资源
            //方式一：
//            try {
//                if(fw != null)
//                    fw.close();
//            } catch (IOException e) {
//                e.printStackTrace();
//            }finally{
//                try {
//                    if(fr != null)
//                        fr.close();
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            }
            //方式二：
            try {
                if(fw != null)
                    fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            try {
                if(fr != null)
                    fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
    //实现文件复制的方法
    public void copyFileWithBuffered(String srcPath,String destPath){
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        try {
            //1.造文件
            File srcFile = new File(srcPath);
            File destFile = new File(destPath);
            //2.造流
            //2.1 造节点流
            FileInputStream fis = new FileInputStream((srcFile));
            FileOutputStream fos = new FileOutputStream(destFile);
            //2.2 造缓冲流
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);

            //3.复制的细节：读取、写入
            byte[] buffer = new byte[1024];
            int len;
            while((len = bis.read(buffer)) != -1){
                bos.write(buffer,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.资源关闭
            //要求：先关闭外层的流，再关闭内层的流
            if(bos != null){
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
            if(bis != null){
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
            //说明：关闭外层流的同时，内层流也会自动的进行关闭。关于内层流的关闭，我们可以省略.
//        fos.close();
//        fis.close();
        }
    }

    /**
     * 1.NIO（New IO）:
     *    是从Java 1.4版本开始引入的一个新的IO API，可以替代标准的Java IO API。
     *    NIO与原来的IO有同样的作用和目的，但是使用的方式完全不同，NIO支持面向缓冲区的、基于通道的IO操作。
     *    NIO将以更加高效的方式进行文件的读写操作。
     * 2.Java NIO 与IO 的主要区别:
     *    1.IO     面向流(Stream Oriented)       阻塞IO(Blocking IO)        (无)选择器
     *    2.NIO    面向缓冲区(Buffer Oriented)   非阻塞IO(NonBlocking IO)    选择器(Selectors)
     * 3.通道（Channel）与缓冲区（Buffer）:
     *    ①.Java NIO系统的核心在于：通道(Channel)和缓冲区(Buffer)。
     *    ②.通道表示打开到IO 设备(例如：文件、套接字)的连接。
     *      若需要使用NIO 系统，需要获取用于连接IO 设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区，对数据进行处理。
     *    注：简而言之，Channel 负责传输，Buffer 负责存储
     * 4.缓冲区（Buffer）
     *    1.缓冲区（Buffer）：一个用于特定基本数据类型的容器。由java.nio 包定义的，所有缓冲区都是Buffer 抽象类的子类。
     *    2.Java NIO中的Buffer 主要用于与NIO 通道进行交互，数据是从通道读入缓冲区，从缓冲区写入通道中的。
     *    3.Buffer 就像一个数组，可以保存多个相同类型的数据。根据数据类型不同(boolean 除外) ，
     *      有以下Buffer 常用子类：
     *      ByteBuffer
     *      CharBuffer
     *      ShortBuffer
     *      IntBuffer
     *      LongBuffer
     *      FloatBuffer
     *      DoubleBuffer
     *      上述Buffer 类他们都采用相似的方法进行管理数据，只是各自管理的数据类型不同而已。
     *      都是通过如下方法获取一个Buffer 对象：
     *          static XxxBuffer allocate(int capacity) : 创建一个容量为capacity 的XxxBuffer 对象
     * 5.缓冲区的基本属性：
     *    1.Buffer 中的重要概念：
     *      ①.容量(capacity) ：表示Buffer 最大数据容量，缓冲区容量不能为负，并且创建后不能更改。
     *      ②.限制(limit)：第一个不应该读取或写入的数据的索引，即位于limit 后的数据不可读写。缓冲区的限制不能为负，并且不能大于其容量。
     *      ③.位置(position)：下一个要读取或写入的数据的索引。缓冲区的位置不能为负，并且不能大于其限制
     *      ④.标记(mark)与重置(reset)：标记是一个索引，通过Buffer 中的mark() 方法指定Buffer 中一个特定的position，
     *        之后可以通过调用reset() 方法恢复到这个position.
     *      注：标记、位置、限制、容量遵守以下不变式：0<=mark<=position<=limit<=capacity
     *    2.Buffer 的常用方法：
     *      Bufferclear() 清空缓冲区并返回对缓冲区的引用
     *      Buffer flip() 将缓冲区的界限设置为当前位置，并将当前位置充值为0
     *      int capacity() 返回Buffer 的capacity大小
     *      boolean hasRemaining() 判断缓冲区中是否还有元素
     *      int limit() 返回Buffer 的界限(limit) 的位置
     *      Bufferlimit(int n) 将设置缓冲区界限为n, 并返回一个具有新limit 的缓冲区对象
     *      Buffer mark() 对缓冲区设置标记
     *      int position() 返回缓冲区的当前位置position
     *      Buffer position(int n) 将设置缓冲区的当前位置为n , 并返回修改后的Buffer 对象
     *      int remaining() 返回position 和limit 之间的元素个数
     *      Buffer reset() 将位置position 转到以前设置的mark 所在的位置
     *      Buffer rewind() 将位置设为为0，取消设置的mark
     * 6.缓冲区的数据操作:
     *    1.Buffer 所有子类提供了两个用于数据操作的方法：get() 与put() 方法
     *    2.获取Buffer 中的数据:
     *      get() ：读取单个字节
     *      get(byte[] dst)：批量读取多个字节到dst 中
     *      get(int index)：读取指定索引位置的字节(不会移动position)
     *    3.放入数据到Buffer 中:
     *      put(byte b)：将给定单个字节写入缓冲区的当前位置
     *      put(byte[] src)：将src 中的字节写入缓冲区的当前位置
     *      put(int index, byte b)：将指定字节写入缓冲区的索引位置(不会移动position)
     * 7.直接与非直接缓冲区:
     *    1.字节缓冲区要么是直接的，要么是非直接的。如果为直接字节缓冲区，则Java 虚拟机会尽最大努力直接在此缓冲区上执行本机I/O 操作。
     *      也就是说，在每次调用基础操作系统的一个本机I/O 操作之前（或之后），
     *      虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中（或从中间缓冲区中复制内容）。
     *    2.直接字节缓冲区可以通过调用此类的allocateDirect() 工厂方法来创建。
     *      此方法返回的缓冲区进行分配和取消分配所需成本通常高于非直接缓冲区。
     *      直接缓冲区的内容可以驻留在常规的垃圾回收堆之外，因此，它们对应用程序的内存需求量造成的影响可能并不明显。
     *      所以，建议将直接缓冲区主要分配给那些易受基础系统的本机I/O 操作影响的大型、持久的缓冲区。
     *      一般情况下，最好仅在直接缓冲区能在程序性能方面带来明显好处时分配它们。
     *    3.直接字节缓冲区还可以通过FileChannel 的map() 方法将文件区域直接映射到内存中来创建。
     *      该方法返回MappedByteBuffer。Java 平台的实现有助于通过JNI 从本机代码创建直接字节缓冲区。
     *      如果以上这些缓冲区中的某个缓冲区实例指的是不可访问的内存区域，则试图访问该区域不会更改该缓冲区的内容，
     *      并且将会在访问期间或稍后的某个时间导致抛出不确定的异常。
     *    4.字节缓冲区是直接缓冲区还是非直接缓冲区可通过调用其isDirect()方法来确定。提供此方法是为了能够在性能关键型代码中执行显式缓冲区管理。
     * 8.通道（Channel）:
     *    1.通道（Channel）：由java.nio.channels 包定义的。Channel 表示IO 源与目标打开的连接。
     *      Channel 类似于传统的“流”。只不过Channel 本身不能直接访问数据，Channel 只能与Buffer 进行交互。
     *    2.Java 为Channel 接口提供的最主要实现类如下：
     *      •FileChannel：用于读取、写入、映射和操作文件的通道。
     *      •DatagramChannel：通过UDP 读写网络中的数据通道。
     *      •SocketChannel：通过TCP 读写网络中的数据。
     *      •ServerSocketChannel：可以监听新进来的TCP 连接，对每一个新进来的连接都会创建一个SocketChannel。
     * 9.获取通道:
     *    1.获取通道的一种方式是对支持通道的对象调用getChannel() 方法。支持通道的类如下：
     *      FileInputStream
     *      FileOutputStream
     *      RandomAccessFile
     *      DatagramSocket
     *      Socket
     *      ServerSocket
     *    注：获取通道的其他方式是使用Files 类的静态方法newByteChannel() 获取字节通道。或者通过通道的静态方法open() 打开并返回指定通道。
     * 10.通道的数据传输：
     *    1.将Buffer 中数据写入Channel
     *      例：int bytesWritten = inChannel.weite(buf);
     *    2.从Channel 读取数据到Buffer
     *      例：int bytesRead = inChannel.read(buf);
     * 11.分散(Scatter)和聚集(Gather):
     *    1.分散读取（Scattering Reads）是指从Channel 中读取的数据“分散”到多个Buffer 中。
     *      注意：按照缓冲区的顺序，从Channel 中读取的数据依次将Buffer 填满。
     *    2.聚集写入（Gathering Writes）是指将多个Buffer 中的数据“聚集”到Channel。
     *      注意：按照缓冲区的顺序，写入position 和limit 之间的数据到Channel 。
     *
     */
    public void Nio(){

    }
    /**
     * 缓冲区（Buffer）
     */
    public void testBuffer(){
        //1. 分配指定大小的缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);
    }

}